package org.xxpay.pay.channel.sqpay;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.TypeReference;
import org.apache.http.HttpEntity;
import org.apache.http.NameValuePair;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.util.EntityUtils;
import org.springframework.stereotype.Service;
import org.xxpay.core.common.domain.api.msg.AgentPayBalanceRetMsg;
import org.xxpay.core.common.domain.api.msg.QueryRetMsg;
import org.xxpay.core.common.util.AmountUtil;
import org.xxpay.core.common.util.MD5Util;
import org.xxpay.core.common.util.MyLog;
import org.xxpay.core.entity.BankCardBin;
import org.xxpay.core.entity.TransOrder;
import org.xxpay.pay.channel.BaseTrans;
import org.xxpay.pay.channel.sqpay.util.CFCARAUtil;
import org.xxpay.pay.channel.sqpay.util.DateUtils;

import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.util.*;

@Service
public class SqpayTransService extends BaseTrans {

    private static final MyLog _log = MyLog.getLog(SqpayTransService.class);

    private final static String charset = "UTF-8";

    @Override
    public String getChannelName() { return SqpayConfig.CHANNEL_NAME_SQPAY; }

    @Override
    public QueryRetMsg trans(TransOrder transOrder) {
        String logPrefix = "【双乾代付】";
        SqpayConfig sqpayConfig = new SqpayConfig(getTransParam(transOrder));
        String merNo = sqpayConfig.getMerNo();                                              // 商户号
        String time = DateUtils.strDate("yyyyMMddHHmmss");                                  // 发起时间
        String content = getContent(transOrder);                                            // 内容
        String signature = "";                                                              // 签名
        String reqUrl = sqpayConfig.getReqUrl();	                                        // 接口地址
        String url = reqUrl + "/merchant/numberPaidSingle.action";
        if(reqUrl.endsWith("/")) {
            url = reqUrl + "merchant/numberPaidSingle.action";
        }
        CloseableHttpClient httpclient = HttpClients.createDefault();                       // 创建默认的httpClient实例
        HttpPost httppost = new HttpPost(url);                                              // 创建httppost

        Map<String, Object> resultMap = new LinkedHashMap<String, Object>();
        resultMap.put("merno", merNo);
        resultMap.put("time", time);
        resultMap.put("content", content);

        String privateKey = sqpayConfig.getPrivateKey();        // 商户私钥
        String publicKey = sqpayConfig.getSqPublicKey();        // 双乾公钥


        try {
            String beforeSignedData = joinMapValue(resultMap, '&');
            signature = CFCARAUtil.signMessageByP1AndPriKey(beforeSignedData, privateKey);
        } catch (Exception e2) {
            _log.error(e2, "生成签名失败");
            throw new RuntimeException("生成签名失败");
        }

        // 创建参数队列
        List<NameValuePair> formparams = new ArrayList<NameValuePair>();
        formparams.add(new BasicNameValuePair("merno", merNo));
        formparams.add(new BasicNameValuePair("time", time));
        formparams.add(new BasicNameValuePair("content", content));
        formparams.add(new BasicNameValuePair("signature", signature));

        UrlEncodedFormEntity uefEntity;
        try {
            uefEntity = new UrlEncodedFormEntity(formparams, "UTF-8");
            httppost.setEntity(uefEntity);
            _log.info("{}executing request: {}", logPrefix, httppost.getURI());
            CloseableHttpResponse response = httpclient.execute(httppost);
            try {
                HttpEntity entity = response.getEntity();
                if (entity != null) {
                    String entitys = EntityUtils.toString(entity, "UTF-8");
                    _log.info("{}Response content: {}", logPrefix, entitys);

                    HashMap<String, Object> data = new LinkedHashMap<String, Object>();
                    LinkedHashMap<String, String> json = JSON.parseObject(entitys, new TypeReference<LinkedHashMap<String, String>>() {});
                    for(Map.Entry<String, String> entry : json.entrySet()){
                        String key = entry.getKey();
                        if(key.equals("signature")||key.equals("remark")){
                            continue;
                        }
                        data.put(key, entry.getValue());
                    }

                    String beforeSignedData = joinMapValue(data, '&');
                    String s = (String) json.get("signature");
                    Boolean result = CFCARAUtil.verifyMessageByP1AndPubKey(beforeSignedData, s, publicKey);
                    _log.info("{}Verify result: {}", logPrefix, result);
                    if (result == true){
                        if ("success".equals(data.get("status"))){
                            _log.info("{} >>> 转账中", logPrefix);
                            return QueryRetMsg.waiting();
                        }else if ("false".equals(data.get("status"))){
                            _log.info("{} >>> 转账失败", logPrefix);
                            return QueryRetMsg.confirmFail();

                        }else {
                            _log.info("{} >>> 状态未知 ", logPrefix);
                            return QueryRetMsg.unknown(); // 如果为其他,修改为处理中
                        }
                    }
                }
                return QueryRetMsg.unknown(); //状态未知
            } catch (Exception e) {
                _log.error(e, "转账异常");
                return QueryRetMsg.sysError();
            } finally {
                response.close();
            }
        } catch (ClientProtocolException e) {
            _log.error(e, "转账异常");
            return QueryRetMsg.sysError();
        } catch (UnsupportedEncodingException e1) {
            _log.error(e1, "转账异常");
            return QueryRetMsg.sysError();
        } catch (IOException e) {
            _log.error(e, "转账异常");
            return QueryRetMsg.sysError();
        } finally {
            // 关闭连接,释放资源
            try {
                httpclient.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    public QueryRetMsg query(TransOrder transOrder) {
        String logPrefix = "【双乾代付查询】";
        QueryRetMsg retObj;
        String transOrderId = transOrder.getTransOrderId();
        SqpayConfig sqpayConfig = new SqpayConfig(getTransParam(transOrder));
        String merNo = sqpayConfig.getMerNo();                                              // 商户号
        String orderNo = transOrderId;                                                      // 订单号
        String signature = "";                                                              // 签名
        String reqUrl = sqpayConfig.getReqUrl();	                                        // 接口地址
        String url = reqUrl + "/merchant/numberPaidSingleQuery.action";
        if (reqUrl.endsWith("/")){
            url = reqUrl + "merchant/numberPaidSingleQuery.action";
        }
        CloseableHttpClient httpclient = HttpClients.createDefault();                       // 创建默认的httpClient实例
        HttpPost httppost = new HttpPost(url);                                              // 创建httppost

        Map<String, Object> resultMap = new LinkedHashMap<String, Object>();
        resultMap.put("merno", merNo);
        resultMap.put("orderNo", orderNo);

        String beforeSignedData = joinMapValue(resultMap, '&');
        String privateKey = sqpayConfig.getPrivateKey();        // 商户私钥
        String publicKey = sqpayConfig.getSqPublicKey();        // 双乾公钥

        try {
            signature = CFCARAUtil.signMessageByP1AndPriKey(beforeSignedData, privateKey);
        } catch (Exception e2) {
            _log.error(e2, "");
        }

        // 创建参数队列
        List<NameValuePair> formparams = new ArrayList<NameValuePair>();
        formparams.add(new BasicNameValuePair("merno", merNo));
        formparams.add(new BasicNameValuePair("orderNo", orderNo));
        formparams.add(new BasicNameValuePair("signature", signature));

        UrlEncodedFormEntity uefEntity;
        try {
            uefEntity = new UrlEncodedFormEntity(formparams, "UTF-8");
            httppost.setEntity(uefEntity);
            _log.info("{}executing request: {}", logPrefix, httppost.getURI());
            CloseableHttpResponse response = httpclient.execute(httppost);
            try {
                HttpEntity entity = response.getEntity();
                if (entity != null) {
                    String entitys = EntityUtils.toString(entity, "UTF-8");
                    _log.info("{}Response content: {}", logPrefix, entitys);

                    HashMap<String, Object> data = new LinkedHashMap<String, Object>();
                    LinkedHashMap<String, String> json = JSON.parseObject(entitys, new TypeReference<LinkedHashMap<String, String>>() {});
                    for(Map.Entry<String, String> entry : json.entrySet()){
                        String key = entry.getKey();
                        if(key.equals("signature")||key.equals("remark")){
                            continue;
                        }
                        data.put(key, entry.getValue());
                    }

                    String responseBeforeSignedData = joinMapValue(data, '&');
                    String status = (String) json.get("searchResult");
                    String s = (String) json.get("signature");
                    //status为失败时无验签
                    if(!"failure".equals(status)){
                        Boolean result = CFCARAUtil.verifyMessageByP1AndPubKey(responseBeforeSignedData, s, publicKey);
                        _log.info("{}CFCA Verify result: {}", logPrefix, result);
                        if (result == true){
                            if ("success".equals(status)){
                                _log.info("{} >>> 查询成功", logPrefix);

                                retObj = QueryRetMsg.confirmSuccess(null);
                                retObj.setChannelOriginResponse(entitys);
                                return retObj;

                            }else {
                                _log.info("{} >>> 查询失败", logPrefix);

                                retObj = QueryRetMsg.confirmFail();
                                retObj.setChannelOriginResponse(entitys);
                                return retObj;
                            }
                        }
                    }

                    retObj = QueryRetMsg.unknown();
                    retObj.setChannelOriginResponse(entitys);
                    return retObj;

                }else{
                    return QueryRetMsg.unknown(); //状态未知
                }
            } catch (Exception e) {
                _log.error(e, "双乾代付查询异常");
                return QueryRetMsg.sysError();

            } finally {
                response.close();
            }
        } catch (ClientProtocolException e) {
            _log.error(e, "双乾代付查询异常");
            return QueryRetMsg.sysError();
        } catch (UnsupportedEncodingException e1) {
            _log.error(e1, "双乾代付查询异常");
            return QueryRetMsg.sysError();
        } catch (IOException e) {
            _log.error(e, "双乾代付查询异常");
            return QueryRetMsg.sysError();
        } finally {
            // 关闭连接,释放资源
            try {
                httpclient.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    @Override
    public AgentPayBalanceRetMsg balance(String payParam) {
        String logPrefix = "【双乾余额查询】";
        SqpayConfig sqpayConfig = new SqpayConfig(payParam);
        String reqUrl = sqpayConfig.getReqUrl();	                                        // 接口地址
        String url = reqUrl + "/merchant/balanceQuery.action";
        if (reqUrl.endsWith("/")){
            url = reqUrl + "merchant/balanceQuery.action";
        }
        String MerNo = sqpayConfig.getMerNo();                                              // 商户号
        String sqBalanceKey = sqpayConfig.getSqBalanceKey();                                // 余额查询密码
        String MD5Info = MD5Util.string2MD5("MerNo=" + MerNo + "&" + MD5Util.string2MD5(sqBalanceKey).toUpperCase()).toUpperCase();
        CloseableHttpClient httpclient = HttpClients.createDefault();                       // 创建默认的httpClient实例
        HttpPost httppost = new HttpPost(url);                                              // 创建httppost

        // 创建参数队列
        List<NameValuePair> formparams = new ArrayList<NameValuePair>();
        formparams.add(new BasicNameValuePair("MerNo", MerNo));
        formparams.add(new BasicNameValuePair("MD5Info", MD5Info));

        UrlEncodedFormEntity uefEntity;
        try {
            uefEntity = new UrlEncodedFormEntity(formparams, "UTF-8");
            httppost.setEntity(uefEntity);
            _log.info("{}executing request: {}", logPrefix, httppost.getURI());
            CloseableHttpResponse response = httpclient.execute(httppost);
            HttpEntity entity = response.getEntity();
            if (entity != null) {
                String entitys = EntityUtils.toString(entity, "UTF-8");
                _log.info("{}Response content: {}", logPrefix, entitys);
                JSONObject json = JSON.parseObject(entitys, new TypeReference<JSONObject>() {});
                String status = (String) json.get("searchResult");
                if ("success".equals(status)){

                    return AgentPayBalanceRetMsg.success(
                            Long.parseLong(AmountUtil.convertDollar2Cent(json.getString("accountBalance"))), // 现金余额
                            Long.parseLong(AmountUtil.convertDollar2Cent(json.getString("accountBalance"))) // 代付可用余额
                    );

                }else {
                    return AgentPayBalanceRetMsg.fail();
                }
            }
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        } catch (ClientProtocolException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return AgentPayBalanceRetMsg.fail();
    }

    public static void main(String[] args) throws Exception {
    }

    /**
     * 签名前的字符串
     * @param map
     * @param connector
     * @return
     */
    public static String joinMapValue(Map<String, Object> map, char connector)	{
        StringBuffer b = new StringBuffer();
        for (Map.Entry<String, Object> entry : map.entrySet()){
            b.append(entry.getKey());
            b.append('=');
            if (entry.getValue() != null){
                b.append(entry.getValue());
            }
            b.append(connector);
        }
        return b.toString().substring(0, b.length()-1);
    }

    private String getContent(TransOrder transOrder){
        String bankName = transOrder.getAccountName();

        // 通过卡bin查询到银行卡信息
        // TODO 可以扩展卡bin表,增加每个通道的银行编号信息
        BankCardBin bankCardBin = commonService.bankCardBinService.findByCardNoAndIfTypeCode(transOrder.getAccountNo(),getChannelName());
        String bankCode = bankCardBin.getBankCode();
        //String bankCode = "1001";
        String bankNo = transOrder.getAccountNo();

        String type = (transOrder.getAccountAttr()+1) + "";
        String amount = AmountUtil.convertCent2Dollar(transOrder.getAmount()+"");
        String transOrderId = transOrder.getTransOrderId();
        String city = "000";
        String mark = "";

        StringBuffer contentData = new StringBuffer();
        contentData.append(bankName);
        contentData.append("|" + bankCode);
        contentData.append("|" + bankNo);
        contentData.append("|" + type);
        contentData.append("|" + amount);
        contentData.append("|" + transOrderId);
        contentData.append("|" + city);
        contentData.append("|" + mark);

        return contentData.toString();
    }

}
